package org.fujay.langchain4jserver.application.service.impl;

import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.fujay.langchain4jserver.application.assembler.ApplicationAssembler;
import org.fujay.langchain4jserver.application.command.CreateApplicationCommand;
import org.fujay.langchain4jserver.application.command.UpdateApplicationConfigurationCommand;
import org.fujay.langchain4jserver.application.dto.ApplicationDetailDTO;
import org.fujay.langchain4jserver.application.dto.ApplicationSummaryDTO;
import org.fujay.langchain4jserver.application.service.ApplicationService;
import org.fujay.langchain4jserver.domain.application.aggregate.AppAggregate;
import org.fujay.langchain4jserver.domain.application.exception.ApplicationInUseException;
import org.fujay.langchain4jserver.domain.application.exception.ApplicationNotFoundException;
import org.fujay.langchain4jserver.domain.application.repository.AppRepository;
import org.fujay.langchain4jserver.domain.application.vo.AppId;
import org.fujay.langchain4jserver.domain.conversation.repository.ConversationRepository;
import org.fujay.langchain4jserver.domain.model.aggregate.Model;
import org.fujay.langchain4jserver.domain.model.exception.ModelNotFoundException;
import org.fujay.langchain4jserver.domain.model.repository.ModelRepository;
import org.fujay.langchain4jserver.domain.model.vo.ModelId;
import org.fujay.langchain4jserver.domain.model.vo.ModelType;
import org.fujay.langchain4jserver.domain.model.vo.TestResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

/**
 * 应用应用服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ApplicationServiceImpl implements ApplicationService {

    private final AppRepository appRepository;
    private final ConversationRepository conversationRepository;
    private final ModelRepository modelRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApplicationDetailDTO createApplication(CreateApplicationCommand command) {
        log.info(
                "开始创建应用: 名称={}, 模型ID={}, 使用知识库={}",
                command.getAppName(),
                command.getModelId(),
                command.getUseKnowledgeBase());

        AppId appId = AppId.create();
        validateLLmModel(command.getModelId());
        AppAggregate aggregate =
                AppAggregate.create(
                        appId, command.getAppName(), command.getAppDesc(), command.getModelId(), null);

        if (command.getUseKnowledgeBase()) {
            if (command.getKnowledgeBaseId() == null) {
                throw new IllegalArgumentException("当 useKnowledgeBase 为 true 时，knowledgeBaseId 不能为空");
            }
            aggregate.updateConfiguration(
                    command.getModelId(), true, command.getKnowledgeBaseId(), "设置初始知识库", null);
        }

        appRepository.save(aggregate);
        log.info("应用创建成功, ID: {}", appId.value());
        return ApplicationAssembler.toDetailDTO(aggregate);
    }

    @Override
    public Optional<ApplicationDetailDTO> getApplication(AppId appId) {
        log.debug("获取应用详情, ID: {}", appId.value());
        return appRepository.findById(appId).map(ApplicationAssembler::toDetailDTO);
    }

    @Override
    public List<ApplicationSummaryDTO> listApplications() {
        log.debug("列出所有应用概要信息");
        List<AppAggregate> aggregates = appRepository.findAllShallow();
        return ApplicationAssembler.toSummaryDTOList(aggregates);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateApplicationConfiguration(UpdateApplicationConfigurationCommand command) {
        AppId appId = command.getAppId();
        log.info("开始更新应用配置, ID: {}", appId.value());

        if(ObjectUtil.isNotNull(command.getNewModelId())){
            validateLLmModel(command.getNewModelId());
        }
        AppAggregate aggregate =
                appRepository.findById(appId).orElseThrow(() -> new ApplicationNotFoundException(appId));

        aggregate.updateBasicInfo(command.getNewAppName(), command.getNewAppDesc());

        aggregate.updateConfiguration(
                command.getNewModelId(),
                command.isUseKb(),
                command.getKbId(),
                command.getChangeDesc(),
                null);

        appRepository.save(aggregate);
        log.info("应用配置更新成功, ID: {}", appId.value());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteApplication(AppId appId) {
        log.info("开始删除应用, ID: {}", appId.value());

        if (!appRepository.existsById(appId)) {
            log.warn("尝试删除不存在的应用, ID: {}", appId.value());
            throw new ApplicationNotFoundException(appId);
        }
        if (conversationRepository.hasActiveConversations(appId)) {
            log.warn("尝试删除的应用存在活动会话, ID: {}", appId.value());
            throw new ApplicationInUseException(appId, "存在活动会话");
        }
        appRepository.deleteById(appId);
        log.info("应用删除成功, ID: {}", appId.value());
    }

    private void validateLLmModel(ModelId modelId) {
        if (modelId == null) {
            throw new IllegalArgumentException("必须提供对话模型ID");
        }
        Model model = modelRepository.findById(modelId).orElseThrow(() -> new ModelNotFoundException(modelId));

        if (model.getModelType() != ModelType.CHAT) {
            throw new IllegalArgumentException("指定的模型ID不是对话模型类型: " + modelId.value());
        }

        if (!model.isEnabled()) {
            throw new IllegalArgumentException("指定的模型未启用: " + modelId.value());
        }

        if (model.getConnectionStatus().lastTestResult() != TestResult.SUCCESS) {
            throw new IllegalArgumentException("模型连通性测试未通过: " + modelId.value());
        }
        log.debug("对话模型验证通过: {}", modelId.value());
    }
}
